package com.jiaqiang.chatgpt.infrastructure.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.Writer;

/**
 * 微信公众号发送消息，解析工具类
 */
@Slf4j
public class XmlBeanUtil {

    //xstream扩展,bean转xml自动加上![CDATA[]]

    /**
     * 这段代码是对 XStream 进行扩展，以实现在将 Java 对象转换为 XML 格式时，自动为文本节点添加 <![CDATA[]]> 标记，
     * 同时排除数字型文本节点不加 CDATA 的情况。
     *
     * 方法返回一个定制的 XStream 对象。在这里，使用了 XppDriver，它是 XStream 库中的一个 XML 解析器驱动器，用于生成 XML。
     */
    public static XStream getMyXStream() {
        return new XStream(new XppDriver() {
            // 重写了 XppDriver 的 createWriter 方法，返回了一个 PrettyPrintWriter 对象，用于将 XML 写入到输出流中。
            @Override
            public HierarchicalStreamWriter createWriter(Writer out) {
                return new PrettyPrintWriter(out) {
                    // 对所有xml节点都增加CDATA标记
                    final boolean cdata = true;

                    // 重写了 PrettyPrintWriter 的 startNode 方法。这里没有进行额外的操作，保持了原始行为。
                    @Override
                    public void startNode(String name, Class clazz) {
                        super.startNode(name, clazz);
                    }

                    // 重写了 PrettyPrintWriter 的 writeText 方法。在这里，对文本进行处理，
                    // 如果文本不是数字型且需要加 CDATA，则在文本前后加上 <![CDATA[ 和 ]]> 标记。
                    @Override
                    protected void writeText(QuickWriter writer, String text) {
                        // 用于判断文本是否为非数字型。
                        // 如果是非数字型，则将文本用 <![CDATA[ 和 ]]> 标记起来，以防止其中包含特殊字符而导致 XML 解析错误。
                        //如果是数字型，则直接写入文本，不添加 CDATA 标记。
                        if (cdata && !StringUtils.isNumeric(text)) {
                            writer.write("<![CDATA[");
                            writer.write(text);
                            writer.write("]]>");
                        } else {
                            writer.write(text);
                        }
                    }
                };
            }
        });
    }

    /**
     * bean转成微信的xml消息格式
     * 方法接收一个Java对象作为参数，该对象需要转换为微信XML消息格式。
     */
    public static String beanToXml(Object object) {
        // 获取一个 XStream 对象，XStream 是一个Java对象到XML的序列化工具，
        // 它能够将Java对象序列化成XML格式的字符串，也能将XML格式的字符串反序列化成Java对象。
        XStream xStream = getMyXStream();
        // xStream.alias("xml", object.getClass()) 方法，设置别名，将Java对象的类名映射为XML的根元素名为 “xml”
        xStream.alias("xml", object.getClass());
        // 处理对象的注解，使其能够正确地转换为XML格式。
        xStream.processAnnotations(object.getClass());
        //  方法将Java对象转换为XML格式的字符串。
        String xml = xStream.toXML(object);
        if (!StringUtils.isEmpty(xml)) {
            return xml;
        } else {
            return null;
        }
    }

    /**
     * xml转成bean泛型方法
     * resultXml 表示待转换的XML格式字符串，clazz 表示目标Java对象的类。
     */
    public static <T> T xmlToBean(String resultXml, Class<T> clazz) {
        log.error("开始转换");
        // 创建一个 XStream 对象，并通过 new DomDriver() 设置其使用 DOM 解析器。
        // XStream 是一个Java对象到XML的序列化工具，它能够将XML格式的字符串反序列化成Java对象
        XStream stream = new XStream(new DomDriver());
        //  方法设置默认的安全防护，确保只允许反序列化指定的类，防止不安全的类加载。
        XStream.setupDefaultSecurity(stream);
        // 方法设置允许反序列化的类，这里只允许反序列化 clazz 类型的对象。
        stream.allowTypes(new Class[]{clazz});
        //  方法处理对象的注解，使其能够正确地从XML格式字符串中反序列化。
        stream.processAnnotations(new Class[]{clazz});
        // 方法设置对象引用模式为 NO_REFERENCES，即不处理对象引用关系，避免出现循环引用导致的问题。
        stream.setMode(XStream.NO_REFERENCES);
        // 通过 stream.alias("xml", clazz) 方法设置XML根元素名为 “xml”，
        // 并使用 stream.fromXML(resultXml) 方法将XML格式字符串转换为目标Java对象
        stream.alias("xml", clazz);
        log.error("转换完成");
        return (T) stream.fromXML(resultXml);
    }

}
